/*
   Copyright 2007 Marco Slot (marco.slot@gmail.com)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package nl.marcoslot.lib.calc;

import java.util.ArrayList;
/**
 * This class represents an mathematical expression in reverse polish notation. 
 *
 * @author Marco Slot
 */
public class Expression implements Value {
   
   /** Expression in reverse Polish notation **/
   private ArrayList   expression;
   
    /**
     * Constructs a new Expression representing a mathematical expression
     */
   public Expression() {
      init();
   }
   
   /**
    * Empties the expression.
    */
   public void init() {
       expression = new ArrayList();
   }
   
    /**
     * Appends a value or operator to the expression.
     *
     * @param o 
     *    object of type Value or Operator. Other objects are ignored.
     * @return 
     *    this Expression.
     */
   public Expression add(Object o) {
       if(o instanceof Value || o instanceof Operator)
          expression.add(o);
       return this;
   }
   
    /**
     * Evaluates the mathematical expression and returns the result.
     *
     * @throws java.lang.ArithmeticException
     *    When the expression is invalid (ie. doesn't evaluate to a single number)
     * @return 
     *    The outcome of the expression.
     */
   public double value() throws ArithmeticException {
      Object   valueOrOperator;
      Value    value;
      Operator operator;
      double[] stack;
      int      stackPointer;
      
      stack        = new double[expression.size()];
      stackPointer = 0;
      
      for(int i = 0; i < expression.size(); i++) {
         valueOrOperator = expression.get(i);
         
         if(valueOrOperator instanceof Value) {
            value = (Value) valueOrOperator;
            
            stack[stackPointer++] = value.value();
         } else if(valueOrOperator instanceof Operator) {
            operator  = (Operator) valueOrOperator;
            double v1 = stack[--stackPointer]; /* most right number */
            double v2 = stack[--stackPointer]; /* number left of v1 */
            stack[stackPointer++] = operator.calculate(v2, v1);
         } 
      }
      
      if(stackPointer != 1) {
          throw new ArithmeticException();
      }
      
      return stack[0];
   }
   
}

